#ifndef DMA_H
#define DMA_H

#ifdef __cplusplus
extern "C" {
#endif

//*****************************************************************************
//
//! \addtogroup dma_api DMA
//! @{
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_dma.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "cpu.h"
#include "debug.h"

#include "f28p65x_device.h"

typedef enum
{
    DMA_M2M_DMA = 0x0,
    DMA_M2P_DMA = 0x1,
    DMA_P2M_DMA = 0x2,
    DMA_P2P_DMA = 0x3,
    DMA_M2P_SRC = 0x4,
    DMA_P2P_SRC = 0x5,
    DMA_P2M_DST = 0x6,
    DMA_P2P_DST = 0x7
} DMA_TtFc;

typedef enum
{
    DMA_TRANS_WIDTH_8  = 0x0,
    DMA_TRANS_WIDTH_16 = 0x1,
    DMA_TRANS_WIDTH_32 = 0x2,
} DMA_TransferWidth;

typedef enum
{
    DMA_MSIZE_1  = 0x0,
    DMA_MSIZE_4  = 0x1,
    DMA_MSIZE_8  = 0x2,
    DMA_MSIZE_16 = 0x3,
    DMA_MSIZE_32 = 0x4,
} DMA_BurstLength;

typedef enum
{
    DMA_INC_TYPE_INCREMENT = 0,
    DMA_INC_TYPE_NO_CHANGE = 1
} DMA_IncrementType;

typedef enum
{
    DMA_MBLK_TYPE_CONTIGUOUS = 0,
    DMA_MBLK_TYPE_RELOAD     = 1,
    DMA_MBLK_TYPE_SHADOW_REG = 2,
    DMA_MBLK_TYPE_LL         = 3
} DMA_MultBlockType;

typedef enum
{
    DMA_IRQ_CH1_ACTIVE    = BIT(0),
    DMA_IRQ_CH2_ACTIVE    = BIT(1),
    DMA_IRQ_CH3_ACTIVE    = BIT(2),
    DMA_IRQ_CH4_ACTIVE    = BIT(3),
    DMA_IRQ_CH5_ACTIVE    = BIT(4),
    DMA_IRQ_CH6_ACTIVE    = BIT(5),
    DMA_IRQ_COMMON_ACTIVE = BIT(16)
} DMA_IrqStatus;

typedef enum
{
    DMA_COMM_IRQ_NONE      = 0,
    DMA_SLVIF_DEC_ERR      = BIT(0),
    DMA_SLVIF_WR2RO_ERR    = BIT(1),
    DMA_SLVIF_RD2WO_ERR    = BIT(2),
    DMA_SLVIF_DRONHOLD_ERR = BIT(3),
    DMA_SLVIF_WRPARITY_ERR = BIT(7),
    DMA_SLVIF_OF_DEC_ERR   = BIT(8)
} DMA_CommIrq;

typedef enum
{
    DMA_IRQ_NONE             = 0,
    DMA_IRQ_BLOCK_TRF        = BIT(0),
    DMA_IRQ_DMA_TRF          = BIT(1),
    DMA_IRQ_SRC_TRAN         = BIT(3),
    DMA_IRQ_DST_TRAN         = BIT(4),
    DMA_IRQ_SRC_DEC_ERR      = BIT(5),
    DMA_IRQ_DST_DEC_ERR      = BIT(6),
    DMA_IRQ_SRC_SLV_ERR      = BIT(7),
    DMA_IRQ_DST_SLV_ERR      = BIT(8),
    DMA_IRQ_LLI_RD_DEC_ERR   = BIT(9),
    DMA_IRQ_LLI_WR_DEC_ERR   = BIT(10),
    DMA_IRQ_LLI_RD_SLV_ERR   = BIT(11),
    DMA_IRQ_LLI_WR_SLV_ERR   = BIT(12),
    DMA_IRQ_INVALID_ERR      = BIT(13),
    DMA_IRQ_MULTIBLKTYPE_ERR = BIT(14),
    DMA_IRQ_DEC_ERR          = BIT(16),
    DMA_IRQ_WR2RO_ERR        = BIT(17),
    DMA_IRQ_RD2RWO_ERR       = BIT(18),
    DMA_IRQ_WRONCHEN_ERR     = BIT(19),
    DMA_IRQ_SHADOWREG_ERR    = BIT(20),
    DMA_IRQ_WRONHOLD_ERR     = BIT(21),
    DMA_IRQ_LOCK_CLEARED     = BIT(27),
    DMA_IRQ_SRC_SUSPENDED    = BIT(28),
    DMA_IRQ_SUSPENDED        = BIT(29),
    DMA_IRQ_DISABLED         = BIT(30),
    DMA_IRQ_ABORTED          = BIT(31),
    DMA_IRQ_ALL_ERR          = ((unsigned long)(0x2F << 15 | 0x2FF << 4)),
    DMA_IRQ_ALL              = 0xFFFFFFFFUL
} DMA_ChIrq;

typedef enum
{
    DMA_HS_HARDWARE = 0,
    DMA_HS_SOFTWARE = 1
} DMA_HandShakeType;

typedef enum
{
    DMA_TRIGGER_NONE            = 0x0000,
    DMA_TRIGGER_MEM             = 0x0000,
    DMA_TRIGGER_ADCA1           = 0x0001,
    DMA_TRIGGER_SDFM1FLT1       = 0x0101,
    DMA_TRIGGER_AESA_CONTEXTIN  = 0x0201,
    DMA_TRIGGER_ADCA2           = 0x0002,
    DMA_TRIGGER_SDFM1FLT2       = 0x0102,
    DMA_TRIGGER_AESA_CONTEXTOUT = 0x0202,
    DMA_TRIGGER_ADCA3           = 0x0003,
    DMA_TRIGGER_SDFM1FLT3       = 0x0103,
    DMA_TRIGGER_AESA_DATAIN     = 0x0203,
    DMA_TRIGGER_ADCA4           = 0x0004,
    DMA_TRIGGER_SDFM1FLT4       = 0x0104,
    DMA_TRIGGER_AESA_DATAOUT    = 0x0204,
    DMA_TRIGGER_ADCAEVT         = 0x0005,
    DMA_TRIGGER_SDFM2FLT1       = 0x0105,
    DMA_TRIGGER_ADCB1           = 0x0006,
    DMA_TRIGGER_SDFM2FLT2       = 0x0106,
    DMA_TRIGGER_ADCB2           = 0x0007,
    DMA_TRIGGER_SDFM2FLT3       = 0x0107,
    DMA_TRIGGER_ADCB3           = 0x0008,
    DMA_TRIGGER_SDFM2FLT4       = 0x0108,
    DMA_TRIGGER_ADCB4           = 0x0009,
    DMA_TRIGGER_SDFM3FLT1       = 0x0109,
    DMA_TRIGGER_ADCBEVT         = 0x000A,
    DMA_TRIGGER_SDFM3FLT2       = 0x010A,
    DMA_TRIGGER_ADCC1           = 0x000B,
    DMA_TRIGGER_SDFM3FLT3       = 0x010B,
    DMA_TRIGGER_ADCC2           = 0x000C,
    DMA_TRIGGER_SDFM3FLT4       = 0x010C,
    DMA_TRIGGER_ADCC3           = 0x000D,
    DMA_TRIGGER_SDFM4FLT1       = 0x010D,
    DMA_TRIGGER_ADCC4           = 0x000E,
    DMA_TRIGGER_SDFM4FLT2       = 0x010E,
    DMA_TRIGGER_ADCCEVT         = 0x000F,
    DMA_TRIGGER_SDFM4FLT3       = 0x010F,
    DMA_TRIGGER_ADCD1           = 0x0010,
    DMA_TRIGGER_SDFM4FLT4       = 0x0110,
    DMA_TRIGGER_ADCD2           = 0x0011,
    DMA_TRIGGER_ADCD3           = 0x0012,
    DMA_TRIGGER_ADCD4           = 0x0013,
    DMA_TRIGGER_ADCDEVT         = 0x0014,
    DMA_TRIGGER_ADCG1           = 0x0114,
    DMA_TRIGGER_ADCG2           = 0x0214,
    DMA_TRIGGER_ADCG3           = 0x0314,
    DMA_TRIGGER_CLA0_0          = 0x0015,
    DMA_TRIGGER_ADCE1           = 0x0115,
    DMA_TRIGGER_ADCE2           = 0x0215,
    DMA_TRIGGER_ADCEEVT         = 0x0315,
    DMA_TRIGGER_CLA0_1          = 0x0016,
    DMA_TRIGGER_ADCE3           = 0x0116,
    DMA_TRIGGER_ADCE4           = 0x0216,
    DMA_TRIGGER_ADCFEVT         = 0x0316,
    DMA_TRIGGER_CLA0_2          = 0x0017,
    DMA_TRIGGER_ADCF1           = 0x0117,
    DMA_TRIGGER_ADCF2           = 0x0217,
    DMA_TRIGGER_ADCHEVT         = 0x0317,
    DMA_TRIGGER_CLA0_3          = 0x0018,
    DMA_TRIGGER_ADCF3           = 0x0118,
    DMA_TRIGGER_ADCF4           = 0x0218,
    DMA_TRIGGER_ADCH4           = 0x0318,
    DMA_TRIGGER_CLA0_4          = 0x0019,
    DMA_TRIGGER_CLA0_5          = 0x001A,
    DMA_TRIGGER_ADCH1           = 0x011A,
    DMA_TRIGGER_ADCH2           = 0x021A,
    DMA_TRIGGER_ADCH3           = 0x031A,
    DMA_TRIGGER_CLA0_6          = 0x001B,
    DMA_TRIGGER_ADCGEVT         = 0x011B,
    DMA_TRIGGER_ADCG4           = 0x021B,
    DMA_TRIGGER_CLA0_7          = 0x001C,
    DMA_TRIGGER_XINT1           = 0x001D,
    DMA_TRIGGER_EPWM1SOCB       = 0x011D,
    DMA_TRIGGER_EPWM7SOCB       = 0x021D,
    DMA_TRIGGER_EPWM13SOCB      = 0x031D,
    DMA_TRIGGER_XINT2           = 0x001E,
    DMA_TRIGGER_EPWM2SOCB       = 0x011E,
    DMA_TRIGGER_EPWM8SOCB       = 0x021E,
    DMA_TRIGGER_EPWM14SOCB      = 0x031E,
    DMA_TRIGGER_XINT3           = 0x001F,
    DMA_TRIGGER_EPWM3SOCB       = 0x011F,
    DMA_TRIGGER_EPWM9SOCB       = 0x021F,
    DMA_TRIGGER_EPWM15SOCB      = 0x031F,
    DMA_TRIGGER_XINT4           = 0x0020,
    DMA_TRIGGER_EPWM4SOCB       = 0x0120,
    DMA_TRIGGER_EPWM10SOCB      = 0x0220,
    DMA_TRIGGER_EPWM16SOCB      = 0x0320,
    DMA_TRIGGER_XINT5           = 0x0021,
    DMA_TRIGGER_EPWM5SOCB       = 0x0121,
    DMA_TRIGGER_EPWM11SOCB      = 0x0221,
    DMA_TRIGGER_EPWM17SOCB      = 0x0321,
    DMA_TRIGGER_TINT0           = 0x0022,
    DMA_TRIGGER_EPWM6SOCB       = 0x0122,
    DMA_TRIGGER_EPWM12SOCB      = 0x0222,
    DMA_TRIGGER_EPWM18SOCB      = 0x0322,
    DMA_TRIGGER_TINT1           = 0x0023,
    DMA_TRIGGER_SPIA_S_TX       = 0x0123,
    DMA_TRIGGER_SPIB_S_TX       = 0x0223,
    DMA_TRIGGER_TINT2           = 0x0024,
    DMA_TRIGGER_SPIC_S_TX       = 0x0124,
    DMA_TRIGGER_SPID_S_TX       = 0x0224,
    DMA_TRIGGER_SPIA_M_TX       = 0x0224,
    DMA_TRIGGER_FSITXA          = 0x0025,
    DMA_TRIGGER_SPIA_M_RX       = 0x0125,
    DMA_TRIGGER_SPIA_S_RX       = 0x0225,
    DMA_TRIGGER_SPIB_M_TX       = 0x0026,
    DMA_TRIGGER_FSIRXA_DATA     = 0x0126,
    DMA_TRIGGER_SPIB_M_RX       = 0x0226,
    DMA_TRIGGER_SPIB_S_RX       = 0x0027,
    DMA_TRIGGER_FSIRXA          = 0x0127,
    DMA_TRIGGER_SPIC_M_RX       = 0x0227,
    DMA_TRIGGER_SPIC_S_RX       = 0x0327,
    DMA_TRIGGER_SPID_M_TX       = 0x0028,
    DMA_TRIGGER_FSIRXA_PING     = 0x0128,
    DMA_TRIGGER_SPID_M_RX       = 0x0228,
    DMA_TRIGGER_SPID_S_RX       = 0x0328,
    DMA_TRIGGER_CLB1_INT        = 0x0029,
    DMA_TRIGGER_CLB2_INT        = 0x002A,
    DMA_TRIGGER_CLB3_INT        = 0x002B,
    DMA_TRIGGER_CLB4_INT        = 0x002C,
    DMA_TRIGGER_CLB5_INT        = 0x002D,
    DMA_TRIGGER_CLB6_INT        = 0x002E,
    DMA_TRIGGER_UARTA_TX        = 0x002F,
    DMA_TRIGGER_UARTB_RX        = 0x012F,
    DMA_TRIGGER_UARTB_TX        = 0x0030,
    DMA_TRIGGER_UARTA_RX        = 0x0130,
    DMA_TRIGGER_EPG             = 0x0031,
    DMA_TRIGGER_EPWM1SOCA       = 0x0032,
    DMA_TRIGGER_EPWM7SOCA       = 0x0132,
    DMA_TRIGGER_EPWM13SOCA      = 0x0232,
    DMA_TRIGGER_EPWM2SOCA       = 0x0033,
    DMA_TRIGGER_EPWM8SOCA       = 0x0133,
    DMA_TRIGGER_EPWM14SOCA      = 0x0233,
    DMA_TRIGGER_EPWM3SOCA       = 0x0034,
    DMA_TRIGGER_EPWM9SOCA       = 0x0134,
    DMA_TRIGGER_EPWM15SOCA      = 0x0234,
    DMA_TRIGGER_EPWM4SOCA       = 0x0035,
    DMA_TRIGGER_EPWM10SOCA      = 0x0135,
    DMA_TRIGGER_EPWM16SOCA      = 0x0235,
    DMA_TRIGGER_EPWM5SOCA       = 0x0036,
    DMA_TRIGGER_EPWM11SOCA      = 0x0136,
    DMA_TRIGGER_EPWM17SOCA      = 0x0236,
    DMA_TRIGGER_EPWM6SOCA       = 0x0037,
    DMA_TRIGGER_EPWM12SOCA      = 0x0137,
    DMA_TRIGGER_EPWM18SOCA      = 0x0237,
    DMA_TRIGGER_ECAP1           = 0x0038,
    DMA_TRIGGER_FSIRXC          = 0x0138,
    DMA_TRIGGER_ECAP2           = 0x0039,
    DMA_TRIGGER_FSIRXD          = 0x0139,
    DMA_TRIGGER_ECAP3           = 0x003A,
    DMA_TRIGGER_FSIRXB_DATA     = 0x013A,
    DMA_TRIGGER_ECAP4           = 0x003B,
    DMA_TRIGGER_FSIRXB_PING     = 0x013B,
    DMA_TRIGGER_ECAP5           = 0x003C,
    DMA_TRIGGER_FSIRXC_DATA     = 0x013C,
    DMA_TRIGGER_ECAP6           = 0x003D,
    DMA_TRIGGER_FSIRXC_PING     = 0x013D,
    DMA_TRIGGER_ECAP7           = 0x003E,
    DMA_TRIGGER_FSIRXD_DATA     = 0x013E,
    DMA_TRIGGER_FSIRXD_PING     = 0x003F,
    DMA_TRIGGER_FSITXB          = 0x013F,
    DMA_TRIGGER_FSIRXB          = 0x023F,
} DMA_Trigger;

typedef enum
{
    DMA_EMULATION_STOP,
    DMA_EMULATION_FREE_RUN
} DMA_EmulationMode;

typedef enum
{
    DMA_INT_AT_BEGINNING,
    DMA_INT_AT_END
} DMA_InterruptMode;

typedef struct
{
    u32 srcAddr;
    u32 destAddr;
    u32 blockSize;
    DMA_TransferWidth srcWidth;
    DMA_TransferWidth destWidth;
    DMA_BurstLength srcMsize;
    DMA_BurstLength destMsize;
    DMA_Trigger srcHSInterface;
    DMA_Trigger destHSInterface;
    DMA_IncrementType srcInc;
    DMA_IncrementType destInc;
    DMA_MultBlockType srcMultblk;
    DMA_MultBlockType destMultblk;
} DMA_ConfigParams;

typedef struct
{
    uint32_t SAR;
    uint32_t rsvd1;
    uint32_t DSR;
    uint32_t rsvd2;
    uint32_t BLOCK_TS;
    uint32_t rsvd3;
    union LLP_REG LLP;
    uint32_t rsvd4;
    union CTL_L_REG CTL_L;
    union CTL_H_REG CTL_H;
} __attribute__((aligned(64))) DMA_LLI;

#ifdef DEBUG
static inline bool DMA_isBaseValid(uint32_t base)
{
    return ((base == DMA_CH1_BASE) || (base == DMA_CH2_BASE) || (base == DMA_CH3_BASE)
            || (base == DMA_CH4_BASE) || (base == DMA_CH5_BASE) || (base == DMA_CH6_BASE));
}
#endif

static inline void DMA_initController(void)
{
    DmaRegs.CFG.bit.DMAC_EN = 1;
    DmaRegs.CFG.bit.INT_EN  = 1;
}

static inline void DMA_triggerSoftReset(void)
{
    DmaRegs.RESET = 0x1;
}

static inline void DMA_Unsupported_setEmulationMode(DMA_EmulationMode mode)
{ }

static inline void DMA_enableTrigger(uint32_t base)
{
    ASSERT(DMA_isBaseValid(base));
    ((volatile struct DMA_CH_REGS *)base)->CFG2_H.bit.HS_SEL_SRC = 0;
    ((volatile struct DMA_CH_REGS *)base)->CFG2_H.bit.HS_SEL_DST = 0;
}

static inline void DMA_disableTrigger(uint32_t base)
{
    ASSERT(DMA_isBaseValid(base));
    ((volatile struct DMA_CH_REGS *)base)->CFG2_H.bit.HS_SEL_SRC = 1;
    ((volatile struct DMA_CH_REGS *)base)->CFG2_H.bit.HS_SEL_DST = 1;
}

static inline void DMA_Unsupported_forceTrigger(uint32_t base)
{ }

static inline void DMA_Unsupported_clearTriggerFlag(uint32_t base)
{ }

static inline bool DMA_getTransferStatusFlag(uint32_t base)
{
    ASSERT(DMA_isBaseValid(base));
    return ((volatile struct DMA_CH_REGS *)base)->INTSTATUS & 0x2U;
}

static inline bool DMA_getBurstStatusFlag(uint32_t base)
{
    ASSERT(DMA_isBaseValid(base));
    return ((volatile struct DMA_CH_REGS *)base)->INTSTATUS & 0x1U;
}

static inline bool DMA_getRunStatusFlag(uint32_t base)
{
    ASSERT(DMA_isBaseValid(base));
    return DmaRegs.CHEN & (1 << ((base - DMA_CH1_BASE) / 0x100));
}

static inline bool DMA_Unsupported_getOverflowFlag(uint32_t base)
{
    return 0;
}

static inline bool DMA_Unsupported_getTriggerFlagStatus(uint32_t base)
{
    return 0;
}

static inline void DMA_startChannel(u8 channel)
{
    DmaRegs.CHEN = (0x101 << (channel - 1));
}

static inline void DMA_stopChannel(u8 channel)
{
    DmaRegs.CHEN = (0x100 << (channel - 1));
}

static inline u32 DMA_getInterruptStatus(uint32_t base)
{
    return ((volatile struct DMA_CH_REGS *)(base))->INTSTATUS;
}

static inline void DMA_enableInterrupt(uint32_t base, u32 flag)
{
    ((volatile struct DMA_CH_REGS *)(base))->INTSIGNAL_EN |= flag;
}

static inline void DMA_disableInterrupt(uint32_t base, u32 flag)
{
    ((volatile struct DMA_CH_REGS *)(base))->INTSIGNAL_EN &= (~flag);
}

static inline void DMA_Unsupported_enableOverrunInterrupt(uint32_t base)
{ }

static inline void DMA_Unsupported_disableOverrunInterrupt(uint32_t base)
{ }

static inline void DMA_clearErrorFlag(uint32_t base, u32 flag)
{
    ((volatile struct DMA_CH_REGS *)(base))->INTCLEAR = flag;
}

static inline void DMA_Unsupported_setInterruptMode(uint32_t base, DMA_InterruptMode mode)
{ }

static inline void DMA_setPriorityMode(bool ch1IsHighPri)
{
    if (ch1IsHighPri)
        ((volatile struct DMA_CH_REGS *)(DMA_CH1_BASE))->CFG2_H.bit.CH_PRIOR = 5;
    else
        ((volatile struct DMA_CH_REGS *)(DMA_CH1_BASE))->CFG2_H.bit.CH_PRIOR = 0;
}

static inline void DMA_configSourceAddress(uint32_t base, const void *srcAddr)
{
    ASSERT(DMA_isBaseValid(base));
    ((volatile struct DMA_CH_REGS *)base)->SAR = *(u32 *)srcAddr;
}

static inline void DMA_configDestAddress(uint32_t base, const void *destAddr)
{
    ASSERT(DMA_isBaseValid(base));
    ((volatile struct DMA_CH_REGS *)base)->DAR = *(u32 *)destAddr;
}

extern void DMA_configChannel(uint32_t base, const DMA_ConfigParams *config);

extern void DMA_configAddress(uint32_t base, const void *destAddr, const void *srcAddr);

extern void DMA_configBurst(uint32_t base, int16_t srcStep, int16_t destStep);

extern void DMA_configTransfer(
    uint32_t base, uint32_t transferSize, int16_t srcStep, int16_t destStep);

extern void DMA_configMode(uint32_t base, DMA_Trigger srcTrigger, DMA_Trigger destTrigger);

extern void DMA_configIncrementMode(
    uint32_t base, DMA_IncrementType src_inc, DMA_IncrementType dst_inc);

extern void DMA_configMultBlkMode(
    uint32_t base, DMA_MultBlockType src_type, DMA_MultBlockType dst_type);

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************

//*****************************************************************************
//
// Mark the end of the C bindings section for C++ compilers.
//
//*****************************************************************************

#ifdef __cplusplus
}
#endif

#endif
